package com.atguigu.dga.governance.assessor.security;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.dga.governance.assessor.Assessor;
import com.atguigu.dga.governance.bean.AssessParam;
import com.atguigu.dga.governance.bean.GovernanceAssessDetail;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description: 目录文件数据访问权限超过建议值指标
 * 检查该表最高权限的目录或者文件，如果超过文件超过{file_permission}或者目录超过{dir_permission}则给0分 其余给10分
 * @Author: lay
 * @Date: 2024/6/30 9:36
 */
@Component("TABLE_DIR_FILE_PERMISSION")
public class TableDirFilePermissionAssessor extends Assessor {
    @Override
    public void checkProblem(GovernanceAssessDetail governanceAssessDetail, AssessParam assessParam) throws Exception {
        //获取参数
        String paramsJson = assessParam.getGovernanceMetric().getMetricParamsJson();
        JSONObject jsonObject = JSONObject.parseObject(paramsJson);
        //目录权限
        String dirPermission = jsonObject.getString("dir_permission");
        //文件权限
        String filePermission = jsonObject.getString("file_permission");
        
        //fs路径
        String tableFsPath = assessParam.getTableMetaInfo().getTableFsPath();
        //fs
        FileSystem fs = FileSystem.get(new URI(tableFsPath), new Configuration(), assessParam.getTableMetaInfo().getTableFsOwner());
        FileStatus fileStatus = fs.getFileStatus(new Path(tableFsPath));

        //越界集合
        List<String> beyondDirList = new ArrayList<>();
        List<String> beyondFileList = new ArrayList<>();
        
        //当前目录权限
        FsPermission currentPermission = fileStatus.getPermission();
        //检查表路径是否越界
        Boolean bool = checkRWX(currentPermission, dirPermission);
        //越界
        if (bool){
            beyondDirList.add(fileStatus.getPath().toString());
        }
        
        FileStatus[] fileStatuses = fs.listStatus(new Path(tableFsPath));
        //递归校验文件是否越界
        checkDirOrFilePermission(fileStatuses, fs, dirPermission, filePermission, beyondDirList, beyondFileList);
        
        if (beyondDirList.size() > 0 || beyondFileList.size() > 0) {
            //给分
            governanceAssessDetail.setAssessScore(BigDecimal.ZERO);
            //问题项
            governanceAssessDetail.setAssessProblem("文件或者目录越权");
            //考评备注
            governanceAssessDetail.setAssessComment("越权的目录: " + beyondDirList + " , 越权的文件: " + beyondFileList);
        }
    }

    /**
     * 递归判断表路径下所有的文件和目录是否越权
     * @param fileStatuses
     * @param fs
     * @param dirPermission
     * @param filePermission
     * @param beyondDirList
     * @param beyondFileList
     * @throws IOException
     */
    private void checkDirOrFilePermission(FileStatus[] fileStatuses, FileSystem fs, String dirPermission, String filePermission, List<String> beyondDirList, List<String> beyondFileList) throws IOException {
        for (FileStatus status : fileStatuses) {
            FsPermission permission = status.getPermission();
            Boolean checked;
            if (status.isFile()){
                checked = checkRWX(permission, filePermission);
                if (checked){
                    beyondFileList.add(status.getPath().toString());
                }
            } else {
                checked = checkRWX(permission, dirPermission);
                if (checked){
                    beyondDirList.add(status.getPath().toString());
                }
                FileStatus[] subStatus = fs.listStatus(new Path(status.getPath().toString()));
                checkDirOrFilePermission(subStatus, fs, dirPermission, filePermission, beyondDirList, beyondFileList);
            }
        }
    }

    /**
     * 检查给定的目录或者文件的权限是否超过建议值
     * @param currentPermission
     * @param paramPermission
     * @return
     */
    private Boolean checkRWX(FsPermission currentPermission, String paramPermission) {
        int userRWX = paramPermission.charAt(0) - '0';
        int groupRWX = paramPermission.charAt(1) - '0';
        int otherRWX = paramPermission.charAt(2) - '0';
        
        if (currentPermission.getUserAction().ordinal() > userRWX){
            return true;
        } else if (currentPermission.getGroupAction().ordinal() > groupRWX){
            return true;
        } else if (currentPermission.getOtherAction().ordinal() > otherRWX){
            return true;
        }
        
        return false;
    }
}
